/*
 * Copyright (c) 2018-2023 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include "xdrbase.h"
#include "channel.h"
#include "api.h"

/**
 * @brief       API: call JS to get shared resource information from the cluster.
 *
 * @param[in]   resourcereq  #1: query resource request key values
 * @param[in]   master       #2: master(port and name) to be called
 *
 * @retval      not NULL     #1: succeed, or JS reply error
 * @retval      NULL         #2: failed
 *
 * @note        When succeed, QuickPoolResourceReply should be freed by the caller.
 *              When failed, error message is set to reply in this API.
 */
struct QuickPoolResourceReply *
client_get_resourceinfo(struct request *resourcereq, struct poolMaster *master)
{
    if (NULL == resourcereq || 0 == resourcereq->number) {
        log_message(ERR, NOCLASS, "%s: Must specify resource request.", __func__);
	return NULL;
    }

    // check the first key to avoid unknown request from client.
    if (REQUEST_KEY_OPTIONS != resourcereq->keyvalues[0].key) {
        log_message(ERR, NOCLASS, "%s: First key must be option for resource request.",
                    __func__);
	return NULL;
    }

    struct auth auth;
    gf_set_auth(&auth, commandParams[AUTH_KIND].string_value);

    unsigned int size; // for calculating the size of the request.
    size = gf_xdrsize_request(resourcereq);
    size += gf_xdrsize_auth(&auth);
    size += HEADER_LEN;

    char *request;
    request = (char *)calloc(size, sizeof(char));
    if (NULL == request) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", size);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    XDR xdrs;
    xdrmem_create(&xdrs, request, size, XDR_ENCODE);

    struct header tohdr, outhdr;
    gf_init_header(&tohdr);
    tohdr.reqcode = BATCH_RESOURCE_INFO;

    if (!gf_xdr_message(&xdrs, resourcereq, &tohdr, &auth, gf_xdr_request, NULL)) {

        // fail to encode request.
        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_message");
        xdr_destroy(&xdrs);
        FREEUP(request);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_XDR;

        return NULL;
    }

    quickpool_errno = 0;

    char *masterList;
    unsigned int port;
    unsigned int connTimeout;
    unsigned int recvTimeout;    
    if (NULL != master) { // client set master, let's call it directly.
        masterList = master->master;
        port = master->port;
        connTimeout = master->conntimeout;
        recvTimeout = master->recvtimeout;
    } else {
        masterList = qp_getmastername();
        if (NULL == masterList) {
            // JS do not know who is master, let's try g_masterlist.
            masterList = g_masterlist;
        }

        port = g_js_port;
        connTimeout = g_conntimeout;
        recvTimeout = g_recvtimeout;
    }
    
    char *reply;
    int cc, fd;
    cc = gf_callmaster(masterList, port, connTimeout, recvTimeout, g_connretries,
                       XDR_GETPOS(&xdrs), request, &outhdr, &reply, &fd);

    if (0 < fd) {
        gf_chan_close(fd);
    }
    xdr_destroy(&xdrs);
    FREEUP(request);
    gf_xdr_free(gf_xdr_auth, (void *)&auth);

    if (0 > cc) { // communication in trouble.

        log_message(ERR, NOCLASS, STR_FUNC_S_FAIL, __func__, "gf_callmaster", masterList);
        FREEUP(reply);
        return NULL;
    }

    struct QuickPoolResourceReply *resourceReply;
    resourceReply = (struct QuickPoolResourceReply *)calloc(1, sizeof(struct QuickPoolResourceReply));
    if (NULL == resourceReply) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct QuickPoolResourceReply));
        FREEUP(reply);

        return NULL;
    }

    quickpool_errno = outhdr.reqcode;

    xdrmem_create(&xdrs, reply, outhdr.length, XDR_DECODE);
    if (quickpool_errno == 0) {

        // Got resource reply from JS, decode it.
        if (!libxdr_resource_reply(&xdrs, resourceReply, &outhdr, NULL)) {

            log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "libxdr_resource_reply");
            xdr_destroy(&xdrs);
            FREEUP(resourceReply);
            FREEUP(reply);

            return NULL;
        }
    } else {

        // JS replied error message, decode it.
        char *strErrMsg = resourceReply->error_message;
        if (!gf_xdr_string(&xdrs, &strErrMsg, &outhdr, NULL)) {

            log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_string");
            xdr_destroy(&xdrs);
            FREEUP(resourceReply);
            FREEUP(reply);
            quickpool_errno = ERROR_XDR;

            return NULL;
        }
    }

    xdr_destroy(&xdrs);
    FREEUP(reply);

    return resourceReply;
} // end function client_get_resourceinfo

/**
 * @brief       API: call JM to get shared resource information from the cluster.
 *
 * @param[in]   resourcereq  #1: query resource request key values
 * @param[in]   master       #2: master(port and name) to be called
 *
 * @retval      not NULL     #1: succeed, or JM reply error
 * @retval      NULL         #2: failed
 *
 * @note        When succeed, QuickPoolResourceReply should be freed by the caller.
 *              When failed, error message is set to reply in this API.
 */
struct QuickPoolResourceReply *
qp_sharedresourceinfo(struct request *resourcereq, struct poolMaster *master)
{
    if (NULL == resourcereq || 0 == resourcereq->number) {
        log_message(ERR, NOCLASS, "%s: Must specify resource request.", __func__);
	return NULL;
    }

    // check the first key to avoid unknown request from client.
    if (REQUEST_KEY_OPTIONS != resourcereq->keyvalues[0].key) {
        log_message(ERR, NOCLASS, "%s: First key must be option for resource request.",
                    __func__);
	return NULL;
    }

    int options = gf_atoi(resourcereq->keyvalues[0].value, INFINIT_INT, -1);
    if (INFINIT_INT == options) {
        log_message(ERR, NOCLASS, "%s: Option must be an integer equal to or larger than 0.",
                    __func__);
        return NULL;
    }

    struct auth auth;
    gf_set_auth(&auth, baseParams[BASE_AUTH].string_value);

    unsigned int size; // for calculating the size of the request.
    size = gf_xdrsize_request(resourcereq);
    size += gf_xdrsize_auth(&auth);
    size += HEADER_LEN;

    char *request;
    request = (char *)calloc(size, sizeof(char));
    if (NULL == request) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", size);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_MEMORY;
	return NULL;
    }

    XDR xdrs;
    xdrmem_create(&xdrs, request, size, XDR_ENCODE);

    struct header hdr, outhdr;
    gf_init_header(&hdr);
    hdr.reqcode = JM_GET_RESOUINFO;

    if (!gf_xdr_message(&xdrs, resourcereq, &hdr, &auth, gf_xdr_request, NULL)) {

        // fail to encode request.
        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_message");
        xdr_destroy(&xdrs);
        FREEUP(request);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_XDR;

        return NULL;
    }

    char *masterName, *hname;
    unsigned int port;
    unsigned int connTimeout;
    unsigned int recvTimeout;
    if (NULL != master) { // client set master, let's call it directly.
        masterName = master->master;
        port = master->port;
        connTimeout = master->conntimeout;
        recvTimeout = master->recvtimeout;
    } else {
        if (options & LOCAL_ONLY) { /* js call jm */
            if ((hname = qp_getmyhostname()) == NULL) {

                log_message(ERR, NOCLASS, "%s: cannot get my host name", __func__);
                gf_xdr_free(gf_xdr_auth, (void *)&auth);
                FREEUP(request);
                xdr_destroy(&xdrs);
                quickpool_errno = ERROR_NO_HOST;
                return NULL;
            }

            masterName = hname;
        } else {

            masterName = qp_getmastername();
            if (NULL == masterName) {
                // JM do not know who is master, let's try g_masterlist.
                masterName = g_masterlist;
            }
        }

        port = g_jm_port;
        connTimeout = g_conntimeout;
        recvTimeout = g_recvtimeout;
    }

    char *reply;
    int cc, fd;
    cc = gf_callmaster(masterName, port, connTimeout, recvTimeout, g_connretries,
                       XDR_GETPOS(&xdrs), request, &outhdr, &reply, &fd);

    if (0 < fd) {
        gf_chan_close(fd);
    }
    xdr_destroy(&xdrs);
    FREEUP(request);
    gf_xdr_free(gf_xdr_auth, (void *)&auth);

    if (0 > cc) { // communication in trouble.

        FREEUP(reply);
        log_message(ERR, NOCLASS, STR_FUNC_S_FAIL, __func__, "gf_callmaster", masterName);
        return NULL;
    }

    quickpool_errno = outhdr.reqcode;

    struct QuickPoolResourceReply *resourceReply;
    resourceReply = (struct QuickPoolResourceReply *)calloc(1, sizeof(struct QuickPoolResourceReply));
    if (NULL == resourceReply) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct QuickPoolResourceReply));
        FREEUP(reply);

        return NULL;
    }

    // Got resource reply from JM, decode it.
    xdrmem_create(&xdrs, reply, outhdr.length, XDR_DECODE);

    if (!libxdr_resource_reply(&xdrs, resourceReply, &outhdr, NULL)) {

        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "libxdr_resource_reply");
        xdr_destroy(&xdrs);
        FREEUP(resourceReply);
        FREEUP(reply);

        return NULL;
    }

    xdr_destroy(&xdrs);
    FREEUP(reply);

    return (resourceReply);
} // end function qp_sharedresourceinfo
